class AIeditPlugin extends BaseCustomPlugin {
    matchingSuggestion = null; //用于存储返回的内容
    writeContent = ""; // 用于存储输入的内容
    typingTimer = null; // 定义全局的计时器，用于延迟处理输入事件
    // 第二套逻辑,使用请求,来获取内容
    messages = [
        {"role": "system", "content": this.config.system_message}, // 系统消息
        // {"role": "user", "content": this.writeContent},
        // {"role": "assistant", "content": "您可以根据需求选择重点：1.", "prefix": true}
    ];

   // 构造请求参数，包括模型名称、消息内容和是否流式响应
    param = {
        "model": this.config.model, // 模型名称
        "messages": this.messages, 
        "stream": this.config.stream, // 是否流式响应
        // "frequency_penalty": 0,
        "max_tokens": this.config.max_tokens, // 最大返回长度
        // "presence_penalty": 0,
        // "response_format": {
        //     "type": "text"
        // },
        "stop": ["\n"]
    };
    header = {
        "content-type": "application/json",
    }
    url = this.config.apiUrl
    getFence = () => {
        const anchor = this.utils.getAnchorNode();
        if (anchor.length === 0) return;
        const pre = anchor.closest(".md-fences[cid]")[0];
        if (!pre) return;
        const activeLine = pre.querySelector(".CodeMirror-activeline");
        if (!activeLine) return;
        const cid = pre.getAttribute("cid");
        const fence = File.editor.fences.queue[cid];
        if (!fence) return;

        const separator = fence.lineSeparator() || "\\n";
        const cursor = fence.getCursor();
        const lineNum = cursor.line + 1;
        const lastNum = fence.lastLine() + 1;
        return { pre, cid, fence, cursor, lineNum, lastNum, separator }
    }

    // 处理通用消息的构造函数
    messagesBuilder = () => {
        //如果最后一条是AI回复的消息，则追加我的消息即可。
        if (this.messages[this.messages.length-1].name ==null || this.messages[this.messages.length-1].name != "myh") {
            this.messages.push({"role": "assistant", "content": this.writeContent, "prefix": true,"name":"myh"});
            //如果messages超过50行，则删除第二行
            if (this.messages.length > 50) {
                //最大不超过数组长度50行
                this.messages = this.messages.filter((_, index) => index!== 1); // 删除第二行
            }
        }else{ //如果最后一条是我添加的数据，而不是AI回复的，则重置最后的消息
            this.messages[this.messages.length-1].content = this.writeContent; // 更新最后一条消息的内容
        }
    }
    // 专门处理通义千问的消息构造函数
    messagesBuilderQwen = () => {
        if (this.messages[this.messages.length-1].name ==null || this.messages[this.messages.length-1].name != "myh") {
            this.messages.push({"role": "assistant", "content": this.writeContent, "partial": true,"name":"myh"});
        }else{ //如果最后一条是我添加的数据，而不是AI回复的，则重置最后的消息
            this.messages[this.messages.length-1].content = this.writeContent; // 更新最后一条消息的内容
        }
        //如果messages超过50行，则删除第二行,暂时有bug
        if (this.messages.length > 50) {
            this.messages = this.messages.splice(1, 1); // 删除第二行
        }
    }
    //发送请求的函数
    handerFetch = async () => {
        if (this.writeContent == "" || this.writeContent == null) {
            this.matchingSuggestion = ""; // 清空返回内容
            return; // 如果没有输入内容，则不发送请求
        }
        // 检查缓存是否命中  如果命中了缓存,就会直接使用缓存的内容, 但是如果中途按了回车,正常来说缓存也不需要渲染
        const cachedResponse = localStorage.getItem(`AIcache_${this.writeContent}`);
        if (cachedResponse) {
            this.matchingSuggestion = cachedResponse; // 使用缓存内容
            //但是这里如果瞬间按了回车,就不应该渲染了撒
            await this.insertSuggestion(); // 插入建议
            return;
        }
        console.log("历史消息：", this.messages);
        this.utils.fetch(this.url, {
            method: 'POST',
            headers: this.header,
            body: JSON.stringify(this.param)
        })
        .then(response => {
            if (!response.ok) {
                console.error('网络响应失败：', response);
                throw new Error('网络响应失败：' + response.statusText);
            }
            return response.json();
        })
        .then(result => {
            this.matchingSuggestion = result.choices[0].message.content;
            if (this.matchingSuggestion) {
                this.insertSuggestion(); // 插入建议
            } else {
                this.matchingSuggestion = "";
            }
        })
        .catch(error => {
            console.error("请求失败：", error);
        });
    };
    // 插入匹配的建议到页面, 但是打字就会触发,遇到缓存就会存储,但是如果按了回车,就不要匹配了
    insertSuggestion = async () => {
        // 插入匹配的建议到页面
        let domnew = document.createElement("span");
        domnew.className = "ai-suggestion"; // 使用 class 替代 id
        domnew.style.backgroundColor = "#f0f0f0";
        domnew.style.color = "#ff0000";
        domnew.style.fontStyle = "italic";
        //有相应数据或者缓存数据,才会插入
        if (this.matchingSuggestion) {
            domnew.textContent = this.matchingSuggestion;
        }else{
          return;
        }
        // const { pre, cid, fence, cursor, lineNum, lastNum, separator } = this.getFence(); // 获取当前光标所在的代码块
        // debugger
        if(this.getFence()){
            //在里面的span标签后面插入元素
            let pre = this.getFence().pre.querySelector('.CodeMirror-activeline').querySelector('.CodeMirror-line');
            //数据插入到span最后一个标签后面
            pre.children[0].insertAdjacentElement("beforeend",domnew); // 在代码块中插入内容
            // this.getFence().fence.scrollIntoView({ line: this.getFence().lineNum - 1, ch: this.getFence().cursor.ch }); // 滚动到光标位置
            File.editor.restoreLastCursor(this.getFence().cursor); // 滚动到光标位置 
            return
        }
        //如果光标是在P标签 tarName, 则是往内部渲染数据
        if (this.utils.getAnchorNode()[0]&&this.utils.getAnchorNode()[0].nodeName == "P") {
            if (this.utils.getAnchorNode()[0].children[0]) {
                console.log("光标在P标签里面true:",this.utils.getAnchorNode()[0].children[0]); // 获取当前光标所在的文本内容
                //这个标签内部span之后添加元素
                this.utils.getAnchorNode()[0].children[0].insertAdjacentElement("afterend", domnew);
            }else{
                this.utils.getAnchorNode()[0].insertAdjacentElement("afterend", domnew);
            }   
            console.log("光标在P标签里面false:",this.utils.getAnchorNode()[0]);
        }
        else{
            console.log("光标在其他标签里面:",this.utils.getAnchorNode()[0].parentElement); // 获取当前光标所在的文本内容
            //找到光标所在标签的父元素,然后在父元素的下一个兄弟节点插入元素
            // this.utils.getAnchorNode()[0].parentElement.insertAdjacentElement("afterend", domnew); // 在代码块中插入内容
            this.utils.getAnchorNode()[0].insertAdjacentElement("afterend", domnew);
        }
        // File.editor.contextMenu.hide();
        File.editor.restoreLastCursor();   
    };

    //处理键盘输入事件
    handleInputEvent = (event) => {
        if(event.inputType == "deleteContentBackward"){
            document.querySelectorAll(".ai-suggestion").forEach(el => el.remove()); // 批量删除
            this.matchingSuggestion = null; // 清空返回内容
            return; // 如果是删除内容的事件，则不处理
        }
        clearTimeout(this.typingTimer); // 清除之前的计时器，避免重复触发
        const doneTypingInterval = 800; // 停止输入 800 毫秒后认为输入完毕
        // const { pre, cid, fence, cursor, lineNum, lastNum, separator } = this.getFence(); // 获取当前光标所在的代码块
        //先处理代码块业务
        if(this.getFence()){
            this.writeContent = this.getFence().fence.getLine(this.getFence().lineNum - 1); // 获取当前光标所在的文本内容
            //这是光标位子
            this.getFence().fence.setCursor(this.getFence().cursor); // 设置光标位置
        }else if(this.utils.getAnchorNode()[0]&&this.utils.getAnchorNode()[0].nodeName == "li"){     //这里处理li标签,不看div了
            console.log("光标在li标签里面:",this.utils.getAnchorNode()[0].innerText); // 获取当前光标所在的文本内容
            // this.writeContent = document.querySelectorAll(".CodeMirror-line")[0].outerText; // 获取当前光标所在的文本内容
            // console.log("输入内容在这里:",this.utils.getAnchorNode()[0].parentElement.CodeMirror) // 获取当前光标所在的文本内容
        }else{
            // console.log("输入内容在这里:",this.utils.getAnchorNode()[0].innerText); // 获取当前光标所在的文本内容
            this.writeContent = this.utils.getAnchorNode()[0].innerText; // 获取当前光标所在的文本内容
        }
        this.messagesBuilder();
        // this.messagesBuilderQwen(); // 处理通义千问的消息构造函数
        //删除之前回复的内容
        document.querySelectorAll(".ai-suggestion").forEach(el => el.remove()); // 批量删除
        // const this = this;
        // 设置延迟处理逻辑
        this.typingTimer = setTimeout(() => {
            this.returnContent = ""; // 清空返回内容
            try {
                // 匹配建议逻辑
                this.matchingSuggestion = null;
                this.handerFetch(); // 调用请求函数
            } catch (error) {
                console.error("网络测试失败：", error); // 捕获并打印错误
            }
        }, doneTypingInterval); // 延迟执行逻辑
    };
    //处理tab和enter以及Backspace键的事件
    handleTabEvent = (event) => {
        if (event.key === 'Tab') {
            event.stopPropagation(); // 阻止事件冒泡
            if (this.matchingSuggestion) {
                //AI回复的消息，放入之前，去掉最后一个消息的属性 [0].nextSibling
                if (this.messages[this.messages.length-1].prefix) {
                    this.messages[this.messages.length-1].prefix = false; // 去掉最后一个消息的属性
                }else if (this.messages[this.messages.length-1].partial) {
                    this.messages[this.messages.length-1].partial = false; // 去掉最后一个消息的属性
                }else{
                    console.log(this.messages[this.messages.length-1]);
                }
                this.messages.push({"role": "assistant", "content": this.matchingSuggestion});
                //如果是代码块里面 处理方式不一样
                if(this.getFence()){
                    // event.preventDefault(); // 阻止默认的 Tab 键行为
                    //阻止冒泡
                    //tab键不要再内部处理了
                    console.log("光标在代码块里面光标什么样子:",this.getFence().cursor); // 获取当前光标所在的文本内容
                    this.getFence().fence.replaceRange(this.matchingSuggestion, {line: this.getFence().lineNum - 1, ch: this.getFence().cursor.ch}, {line: this.getFence().lineNum - 1, ch: this.getFence().cursor.ch}); // 在代码块中插入内容   
                    // this.utils.insertText(this.getFence().cursor, this.matchingSuggestion, true); // 在代码块中插入内容
                }else{
                    //获取并删除 但是删除的东西不对
                    this.utils.getAnchorNode()[0].nextSibling?.remove(); 
                    // this.utils.getAnchorNode()[0]?.remove(); 
                    console.log("光标在其他标签里面光标什么样子:",this.matchingSuggestion); // 获取当前光标所在的文本内容
                    //正则去掉 **
                    this.utils.insertText(this.utils.getAnchorNode(), this.matchingSuggestion, true);
                }
                // 缓存结果
                if (this.writeContent != " ") {
                    localStorage.setItem(`AIcache_${this.writeContent}`, this.matchingSuggestion);
                }
                this.matchingSuggestion = null;
            }
            //包含删除
        }else if(event.key === 'Enter'|| event.keyCode === "Backspace"){
            //回车后还是不要做任何事情了 我想要实现的是 回车后,可以继续调用AI回复的内容,添加到对应的位子.
            console.log("按下Enter或者Backspace键:",this.matchingSuggestion,document.querySelector(".ai-suggestion"));
            if (this.matchingSuggestion) {
                 this.matchingSuggestion= null; // 清空返回内容
                 //获取并删除 但是删除的东西不对
                //  console.log(this.utils.getAnchorNode()[0],"删除的内容:"); // 获取当前光标所在的文本内容
                 this.utils.getAnchorNode()[0].nextSibling?.remove(); 
                 this.utils.getAnchorNode()[0]?.remove(); 
                 document.querySelectorAll(".ai-suggestion").forEach(el => el?.remove()); // 批量删除
            }else{
                document.querySelectorAll(".ai-suggestion").forEach(el => el.remove()); // 批量删除
            }
        }
    };

    concatUrl = () => {
        //换一下API的请求头 包含
        if(!this.url.includes("127.0.0.1")&&!this.url.includes("localhost")){
            // 如果访问外部API,则需要使用外部的API密钥
            this.header['Authorization'] = `Bearer `+eval(this.config.api_key);
            if (this.url.includes("https://api.openai.com")) {
                this.url = this.url + "/v1/chat/completions"; // 拼接请求this.url
            }else if(this.url.includes("https://api.deepseek.com")){
                this.url = this.url + "/chat/completions"; // 拼接请求this.url
            }
            //通义千问模型 消息结构不太一样,https://bailian.console.aliyun.com/model-market?tab=doc#/doc/?type=model&this.url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2862210.html
            else if(this.url.includes("https://dashscope.aliyuncs.com")){//通义千问
                this.url = this.url + "/compatible-mode/v1/chat/completions"; // 拼接请求this.url
            }
            else{
                //其他通用模型拼接方案
                this.url = this.url + "/chat/completions"; // 拼接请求this.url
            }
        }else{
            //如果是连接的本地的lm-studio,则不需要使用API密钥
            this.url = this.url + "/v1/chat/completions"; // 拼接请求this.url
            this.messagesBuilder();
        }
    }

    beforeProcess = async () => {
        const write = document.querySelector("#write");
        this.concatUrl(); // 拼接请求this.url
        if (write) {
            // 为输入框绑定
            write.addEventListener('keydown', this.handleTabEvent);
             // 为输入框绑定
            write.addEventListener('input', this.handleInputEvent);
        } else {
            console.warn("#write 元素未找到");
        }
    };

    selector = () => '#write [mdtype="heading"]'; // 定义选择器，用于定位特定元素,后续添加在线换模型方案

    style = () => ``;

    html = () => `
        蜗牛学苑 --- AI续写插件-已开启
    `;

    hint = () => "AI续写插件"; // 插件的提示信息

    hotkey = () => [this.config.hotkey_string]; // 注册快捷键

    process = () => {
        // 初始化 AI 知识库
        // localStorage.setItem("AIknowledge", "AI知识库初始化完成！");
    };
}

module.exports = {
    plugin: AIeditPlugin,
};